﻿HmJS.$Import('core.element');

/*
--- 
version: 1.3.1-0
description: Allows your application to selectively load classes and lets you control when you want to instantiate the loaded class.
This is useful if you are building a large website and are packaging all your classes into one JavaScript file.
Sometimes you want some scripts to be deferred and requested only when they are needed.  This is when LazyLoader will be useful to you.
license: MIT-style license
download: http://mootools.net/forge/p/lazyloader
source: http://github.com/REOL/Moo-Lazy-Loader

authors: 
- David Chan CK (http://www.reol.com)

requires: 
- core/1.3: '*'
- more/1.3.1: [Utilities.Assets]
- more/1.3.1: [Class.Refactor]
provides: [LazyLoader]
...
*/

/*
How to use
----------

To load the external class and instantiate it (default behavior):

new LazyLoader('MooClassName', [arg_0, arg_1, ...]);


To load the external class and instantiate it later:

var loader = new LazyLoader('MooClassName', [arg_0, arg_1, ...],
{
autoStart: false
});
loader.addEvent('load', function()
{
// Code
// ...
this.process(myLoader);
}.bind(loader));

To get the reference of an instance:

var loader = new LazyLoader('MooClassName', [arg_0, arg_1, ...]);
loader.addEvent('processEnd', function()
{
this.getInstance().doSomething(); // doSomething is an instance method.
}.bind(loader));

An example that uses some options:

new LazyLoader('MooClassName', [arg_0, arg_1, ...],
{
path: '/javascript/{Klass}.js'
});
	
NEW - To load multiple classes at once

var loader = new LazyLoader.Multiple(
[
'GoogleMaps.Control',
'GoogleMaps.Control.Type',
'GoogleMaps.Control.Zoom',
'GoogleMaps.Marker'
],
{
path: '/javascript/{Klass}.js'
}); 
loader.addEvent('load', this.process_after_loading.bind(this));
  
Syntax
------

new LazyLoader(mooClassName, arguments, [options])
  
Arguments
---------

1. mooClassName - (string) Your class name and also the filename.  Please see options as to how you can change the structure of the file path.
2. arguments - (array) Your arguments that will be passed to the constructor.
  
Options
-------

* autoStart      : (boolean) Whether or not your class will be instantiated once it's loaded.  Defaults to true
* path           : (string) The path to your class, use {Klass} in your string to substitute it to the mooClassName.  Defaults to same path.

Events
------

* onLoad : External script has been loaded.
* onProcessStart: Before processing of the loaded external script.
* onProcessEnd: After processing of the loaded external script.
*/

HmJS.register('util.LazyLoader', function ($ns) {

	var LazyLoader = new Class({

		Implements: [Events, Options],

		options: {
			/*
			onLoad: $empty(thisElement, event),
			onProcessStart: $empty(thisElement, event),
			onProcessEnd: $empty(thisElement, event),
			*/
			autoStart: true,
			path: '{Klass}.js'
		},

		initialize: function (klass, args, options) {
			this.setOptions(options);
			var opts = this.options;

			this.klass = klass;
			this.args = args;
			this.instance;

			this.load();
		},

		emulate: function () {
			var args = arguments;
		},

		instantiate: function () {
			var Klass = this.Klass;
			Class.refactor(Klass, {
				initialize: function () {
					this.previous.apply(this, arguments[0]);
				}
			});
			return new Klass(arguments);
		} .protect(),

		load: function () {
			var opts = this.options, script = opts.path.substitute({ Klass: this.klass });
			new Asset.javascript(script, {
				'onload': this.loaded.bind(this)
			});
		},

		loaded: function () {
			this.fireEvent('load');
			if (this.options.autoStart) {
				this.process();
			}
		},

		process: function () {
			var klass = null;
			this.fireEvent('processStart');
			if (this.instance == undefined) {
				eval('this.Klass = ' + this.klass);
			}
			this.instance = this.instantiate.apply(this, this.args);
			this.fireEvent('processEnd');
		},

		getInstance: function () {
			return this.instance;
		}

	});

	LazyLoader.Multiple = new Class({

		Implements: [Events, Options],

		Extends: LazyLoader,

		initialize: function (klasses, options) {
			this.setOptions(options);
			var opts = this.options;

			this.klasses = klasses;

			this.load(0);
		},

		load: function (i) {
			var opts = this.options, script;

			if (i > this.klasses.length - 1) {
				this.fireEvent('load');
				return;
			}

			script = opts.path.substitute({ Klass: this.klasses[i] });

			new Asset.javascript(script, {
				'onload': this.load.bind(this, i + 1)
			});
		}

	});

	return LazyLoader;

});